/*
 * Copyright 2011 Clean GWT
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.cleangwt.web.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.Window.Location;

/**
 * A {@link RequestCallback} with response state recognition.
 * <ul>
 * <li>The rejected state(0) will do nothing,</li>
 * <li>for the {@link Response#SC_OK} state, the process goes to {@link #onSuccess(String)},</li>
 * <li>or the response with a given Location header, goes to {@link #onRedirect(String, int)},</li>
 * <li>otherwise, gose to{@link #onError(Request, Throwable)}</li>
 * </ul>
 * 
 * <p>
 * if your service invocation has multi-states, may like 'updateSuccess', 'updateSuccessWithStateA',
 * 'someUpdateFaildButItsOk', 'updateFaild', put the state in your response and dispose them in your
 * {@link #onSuccess(String)} instead of throwing exception in your server side. for those
 * exception, the callback process would goes to {@link #onError(Request, Throwable)}, than by
 * default, to your {@link UncaughtExceptionHandler}. Your should not write too much process for the
 * unexpected exceptions, just log it or simply prompt the error message.
 * </p>
 * 
 * @author Jess
 * @version 1.0
 * @since 1.0
 */
public abstract class StatedCallback implements RequestCallback {

  /**
   * Creates a stated callback.
   */
  public StatedCallback() {
  }

  /**
   * Just throws RuntimeException if any error occurs. You may have
   * {@link GWT#setUncaughtExceptionHandler(UncaughtExceptionHandler)} to handler all the runtime
   * exceptions that unexpected.
   */
  @Override
  public void onError(Request request, Throwable exception) {
    throw new RuntimeException(exception);
  }

  @Override
  public void onResponseReceived(Request request, Response response) {
    // ignore canceled request
    if (response.getStatusCode() == 0) {
      return;
    }
    if (response.getStatusCode() == Response.SC_OK) {
      onSuccess(response.getText());
    } else if (response.getHeader("Location") != null) {
      onRedirect(response.getHeader("Location"), response.getStatusCode());
    } else {
      onError(request, new Exception(response.getStatusText() == null ? String.valueOf(response
          .getStatusCode()) : response.getStatusText()));
    }
  }

  /**
   * Called when the status of the HTTP response is not 'OK' and there's a none null value of
   * 'Location' header for redirecting.
   * 
   * @param location the location to redirect
   * @param statusCode the response status
   */
  protected void onRedirect(final String location, int statusCode) {
    Location.assign(location);
  }

  /**
   * Called when the status code of the HTTP response is "OK", 200.
   * 
   * @param result the the response text
   */
  protected abstract void onSuccess(String result);
}
